
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sstream>
#include "porting.h"
#include "httpmsg.h"
#include "utils.h"
#include "../inc/jansson.h"

static char * sp_strsep(char **s, const char *del)
{
	char *d, *tok;
	if (!s || !*s) return NULL;
	tok = *s;
	d = strstr(tok, del);
	if (d) {
		*s = d + strlen(del);
		*d = '\0';
	} else {
		*s = NULL;
	}
	return tok;
}
HttpMsgParser :: HttpMsgParser()
{
	mMessage = NULL;
	mStatus = eStartLine;
	mIgnoreContent = 0;
}
HttpMsgParser :: ~HttpMsgParser()
{
	if( NULL != mMessage ) delete mMessage;
}
void HttpMsgParser :: setIgnoreContent( int ignoreContent )
{
	mIgnoreContent = ignoreContent;
}
int HttpMsgParser :: isIgnoreContent() const
{
	return 0 != mIgnoreContent;
}
int HttpMsgParser :: parseStartLine( HttpMessage ** message,
		const void * buffer, int len )
{
	int lineLen = 0;
	char * pos = (char*)memchr( buffer, '\n', len );
	if( NULL != pos ) {
		lineLen = pos - (char*)buffer + 1;
		char * line = (char*)malloc( lineLen + 1 );
		memcpy( line, buffer, lineLen );
		line[ lineLen ] = '\0';
		pos = line;
		char * first, * second;
		first = sp_strsep( &pos, " " );
		second = sp_strsep( &pos, " " );
		if( 0 == strncasecmp( line, "HTTP", 4 ) ) {
			HttpResponse * response = new HttpResponse();
			if( NULL != first ) response->setVersion( first );
			if( NULL != second ) response->setStatusCode( atoi( second ) );
			if( NULL != pos ) response->setReasonPhrase( strtok( pos, "\r\n" ) );
			*message = response;
		} else {
			HttpRequest * request = new HttpRequest();
			if( NULL != first ) request->setMethod( first );
			if( NULL != second ) request->setURL( second );
			if( NULL != second ) request->setURI( sp_strsep( &second, "?" ) );
			if( NULL != pos ) request->setVersion( strtok( pos, "\r\n" ) );
			char * params = second;
			for( ; NULL != params && '\0' != *params; ) {
				char * value = sp_strsep( &params, "&" );
				char * name = sp_strsep( &value, "=" );
				request->addParam( name, NULL == value ? "" : value );
			}
			*message = request;
		}
		free( line );
	}
	return lineLen;
}
int HttpMsgParser :: parseHeader( HttpMessage * message,
		const void * buffer, int len )
{
	int lineLen = 0;
	char * pos = (char*)memchr( buffer, '\n', len );
	if( NULL != pos ) {
		lineLen = pos - (char*)buffer + 1;
		char * line = (char*)malloc( lineLen + 1 );
		memcpy( line, buffer, lineLen );
		line[ lineLen ] = '\0';
		pos = line;
		char * name = sp_strsep( &pos, ":" );
		if( NULL != pos ) {
			pos = strtok( pos, "\r\n" );
			pos += strspn( pos, " " );
			message->addHeader( name, pos );
		}
		free( line );
	}
	return lineLen;
}
int HttpMsgParser :: getLine( const void * buffer, int len,
		char * line, int size )
{
	int lineLen = 0;
	char * pos = (char*)memchr( buffer, '\n', len );
	if( NULL != pos ) {
		lineLen = pos - (char*)buffer + 1;
		int realLen = size - 1;
		realLen = realLen > lineLen ? lineLen : realLen;
		memcpy( line, buffer, realLen );
		line[ realLen ] = '\0';
		strtok( line, "\r\n" );
	}
	return lineLen;
}
int HttpMsgParser :: parseChunked( HttpMessage * message,
		const void * buffer, int len, int * status )
{
	int parsedLen = 0, hasChunk = 1;
	for( ; 0 != hasChunk && eCompleted != *status; ) {
		hasChunk = 0;
		char chunkSize[ 32 ] = { 0 };
		int lineLen = getLine( ((char*)buffer) + parsedLen, len - parsedLen,
				chunkSize, sizeof( chunkSize ) );
		int contentLen = strtol( chunkSize, NULL, 16 );
		if( contentLen > 0 && ( len - parsedLen ) > ( contentLen + lineLen ) ) {
			int emptyLen = getLine( ((char*)buffer) + parsedLen + lineLen + contentLen,
				len - parsedLen - lineLen - contentLen,
				chunkSize, sizeof( chunkSize ) );
			if( emptyLen > 0 ) {
				parsedLen += lineLen;
				message->appendContent( ((char*)buffer) + parsedLen, contentLen );
				parsedLen += contentLen + emptyLen;
				hasChunk = 1;
			}
		}
		if( 0 == contentLen && lineLen > 0 ) {
			parsedLen += lineLen;
			*status = eCompleted;
		}
	}
	return parsedLen;
}
int HttpMsgParser :: parseContent( HttpMessage * message,
		const void * buffer, int len, int * status )
{
	int parsedLen = 0;
	
	string value = message->getHeaderValue( HttpMessage::HEADER_CONTENT_LENGTH );
	if (value.length()==0)
		value = message->getHeaderValue(HttpMessage::HEADER_CONTENT_LENGTH2);
	int contentLen = atoi(value.size()==0 ? "0" : value.c_str());
	if( contentLen > 0 && len >= contentLen ) {
		message->appendContent( ((char*)buffer), contentLen );
		parsedLen = contentLen;
	}
	if( contentLen == message->getContentLength() ) *status = eCompleted;
	return parsedLen;
}
int HttpMsgParser :: append( const void * buffer, int len )
{
	int parsedLen = 0;
	if( eCompleted == mStatus ) return parsedLen;
	// parse start-line
	if( NULL == mMessage ) {
		parsedLen = parseStartLine( &mMessage, buffer, len );
		if( parsedLen > 0 ) mStatus = eHeader;
	}
	if( NULL != mMessage ) {
		// parse header
		for( int headerLen = 1; eHeader == mStatus
				&& headerLen > 0 && parsedLen < len; parsedLen += headerLen ) {
			headerLen = parseHeader( mMessage, ((char*)buffer) + parsedLen, len - parsedLen );
			char ch = * ( ((char*)buffer) + parsedLen );
			if( '\r' == ch || '\n' == ch ) mStatus = eContent;
		}
		if( HttpMessage::eResponse == mMessage->getType()
			&& eContent == mStatus && mIgnoreContent ) mStatus = eCompleted;
		// parse content
		if( eContent == mStatus ) {
			string encoding = mMessage->getHeaderValue( HttpMessage::HEADER_TRANSFER_ENCODING );
			if (encoding=="chunked") {
				parsedLen += parseChunked( mMessage, ((char*)buffer) + parsedLen,
					len - parsedLen, &mStatus );
			} else {
				parsedLen += parseContent( mMessage, ((char*)buffer) + parsedLen,
					len - parsedLen, &mStatus );
			}
		}
		if( eCompleted == mStatus ) postProcess( mMessage );
	}
	return parsedLen;
}
void HttpMsgParser :: postProcess( HttpMessage * message )
{
	if( HttpMessage::eRequest == message->getType() ) {
		HttpRequest * request = (HttpRequest*)message;
		string contentType = request->getHeaderValue(
			HttpMessage::HEADER_CONTENT_TYPE); 
		if (request->getContentLength() > 0 && contentType=="application/x-www-form-urlencoded") {
			char * content = (char*)malloc( request->getContentLength() + 1 );
			memcpy( content, request->getContent(), request->getContentLength() );
			content[ request->getContentLength() ] = '\0';
			char * params = content;
			for( ; NULL != params && '\0' != *params; ) {
				char * value = sp_strsep( &params, "&" );
				char * name = sp_strsep( &value, "=" );
				request->addParam( name, NULL == value ? "" : value );
			}
			free( content );
		}
		else if (request->getContentLength() > 0 && contentType == "application/json") {

		}
	}
}
int HttpMsgParser :: isCompleted() const
{
	return eCompleted == mStatus;
}

void HttpMsgParser::clear()
{
	mStatus = 0;
	delete mMessage;
	mMessage = NULL;
}

HttpRequest * HttpMsgParser :: getRequest() const
{
	if( NULL != mMessage && HttpMessage::eRequest == mMessage->getType() ) {
		return (HttpRequest*)mMessage;
	}
	return NULL;
}
HttpResponse * HttpMsgParser :: getResponse() const
{
	if( NULL != mMessage && HttpMessage::eResponse== mMessage->getType() ) {
		return (HttpResponse*)mMessage;
	}
	return NULL;
}
//---------------------------------------------------------
const char * HttpMessage :: HEADER_CONTENT_LENGTH = "Content-Length";
const char * HttpMessage::HEADER_CONTENT_LENGTH2 = "content-length";
const char * HttpMessage :: HEADER_CONTENT_TYPE = "Content-Type";
const char * HttpMessage::HEADER_CONTENT_TYPE2 = "content-type";
const char * HttpMessage :: HEADER_CONTENT_LANGUAGE = "Content-Language";
const char * HttpMessage :: HEADER_CONNECTION = "Connection";
const char * HttpMessage :: HEADER_PROXY_CONNECTION = "Proxy-Connection";
const char * HttpMessage :: HEADER_TRANSFER_ENCODING = "Transfer-Encoding";
const char * HttpMessage :: HEADER_DATE = "Date";
const char * HttpMessage :: HEADER_SERVER = "Server";
HttpMessage :: HttpMessage( int type )
	: mType( type )
{
	mContent = NULL;
	mContentLength = 0;
	mMaxLength = 0;
	snprintf( mVersion, sizeof( mVersion ), "%s", "HTTP/1.0" );
}
HttpMessage :: ~HttpMessage()
{
	if( NULL != mContent ) free( mContent );
}
int HttpMessage :: getType() const
{
	return mType;
}
void HttpMessage :: setVersion( const char * version )
{
	snprintf( mVersion, sizeof( mVersion ), "%s", version );
}
const char * HttpMessage :: getVersion() const
{
	return mVersion;
}
void HttpMessage :: appendContent( const void * content, int length, int maxLength )
{
	if( length <= 0 ) length = strlen( (char*)content );
	int realLength = mContentLength + length;
	realLength = realLength > maxLength ? realLength : maxLength;
	if( realLength > mMaxLength ) {
		if( NULL == mContent ) {
			mContent = malloc( realLength + 1 );
		} else {
			mContent = realloc( mContent, realLength + 1 );
		}
		mMaxLength = realLength;
	}
	memcpy( ((char*)mContent) + mContentLength, content, length );
	mContentLength = mContentLength + length;
	((char*)mContent)[ mContentLength ] = '\0';
}
void HttpMessage :: setContent( const void * content, int length )
{
	mContentLength = 0;
	appendContent( content, length );
}
void HttpMessage :: directSetContent( void * content, int length )
{
	if( NULL != mContent ) free( mContent );
	length = length > 0 ? length : strlen( (char*)content );
	mContentLength = mMaxLength = length;
	mContent = content;
}
const void * HttpMessage :: getContent() const
{
	return mContent;
}
int HttpMessage :: getContentLength() const
{
	return mContentLength;
}
void HttpMessage :: addHeader( const char * name, const char * value )
{
	mapHeaderNameValue[name] = string(value);
}
int HttpMessage :: removeHeader( const char * name )
{
	int ret = 0;
	map<string, string>::const_iterator it = mapHeaderNameValue.find(name);
	if (it != mapHeaderNameValue.end())
	{
		mapHeaderNameValue.erase(it);
		ret = 1;
	}
	return ret;
}
int HttpMessage :: removeHeader( int index )
{
	int ret = 0;
	return ret;
}
int HttpMessage :: getHeaderCount() const
{
	return (int)mapHeaderNameValue.size();
}
string HttpMessage :: getHeaderValue( const char * name ) const
{
	map<string, string>::const_iterator it = mapHeaderNameValue.find(name);
	if (it != mapHeaderNameValue.end())
		return it->second;
	return "";
}
int HttpMessage :: isKeepAlive() const
{
	string proxy = getHeaderValue( HEADER_PROXY_CONNECTION );
	string local = getHeaderValue(HEADER_CONNECTION);
	if (proxy=="Keep-Alive" ||  local=="Keep-Alive" ) {
		return 1;
	}
	return 0;
}
//---------------------------------------------------------
HttpRequest :: HttpRequest()
	: HttpMessage( eRequest )
{
	memset( mMethod, 0, sizeof( mMethod ) );
	memset( mClientIP, 0, sizeof( mClientIP ) );
	mURI = mURL = NULL;
}
HttpRequest :: ~HttpRequest()
{
	if( NULL != mURI ) free( mURI );
	if( NULL != mURL ) free( mURL );
}
void HttpRequest :: setMethod( const char * method )
{
	snprintf( mMethod, sizeof( mMethod ), "%s", method );
}
const char * HttpRequest :: getMethod() const
{
	return mMethod;
}
void HttpRequest :: setURI( const char * uri )
{
	char * temp = mURI;
	mURI = strdup( uri );
	if( NULL != temp ) free( temp );
}
const char * HttpRequest :: getURI() const
{
	return mURI;
}
void HttpRequest :: setURL( const char * url )
{
	char * temp = mURL;
	mURL = strdup( url );
	if( NULL != temp ) free( temp );
}
const char * HttpRequest :: getURL() const
{
	return mURL;
}
void HttpRequest :: setClinetIP( const char * clientIP )
{
	snprintf( mClientIP, sizeof( mClientIP ), "%s", clientIP );
}
const char * HttpRequest :: getClientIP() const
{
	return mClientIP;
}
void HttpRequest :: addParam( const char * name, const char * value )
{
	mapParamNameValue[name] = value;
}
int HttpRequest :: removeParam( const char * name )
{
	int ret = 0;
	map<string, string>::const_iterator it = mapParamNameValue.find(name);
	if (it != mapParamNameValue.end())
	{
		mapParamNameValue.erase(it);
		ret = 1;
	}
	return ret;
}
int HttpRequest :: getParamCount() const
{
	return (int)mapParamNameValue.size();
}
string HttpRequest :: getParamValue( const char * name ) const
{
	map<string, string>::const_iterator it = mapParamNameValue.find(name);
	if (it != mapParamNameValue.end())
		return it->second;
	return "";
}
string HttpRequest::getParamList() const
{
	string ret = "";
	map<string, string>::const_iterator it = mapParamNameValue.begin();
	while (it != mapParamNameValue.end())
	{
		ret += it->first;
		ret += ":";
		ret += it->second;
		ret += ",";
		it++;
	}
	return ret;
}

string HttpRequest::getJsonParam() const
{
	string ret = "";
	string contentType = getHeaderValue(
		HttpMessage::HEADER_CONTENT_TYPE);
	if (contentType == "application/json")
	{
		int length=this->getContentLength();
		if (length > 0)
		{
			ret.resize(length);
			memcpy((char*)ret.data(), this->getContent(), length);
		}
	}
	else{
		json_t* obj = json_object();
		map<string, string>::const_iterator it = mapParamNameValue.begin();
		while (it != mapParamNameValue.end())
		{
			json_object_set_new(obj, it->first.c_str(), json_string(it->second.c_str()));
			it++;
		}
		ret = json_dumps(obj, JSON_INDENT(0));
	}
	return ret;
}

//---------------------------------------------------------
HttpResponse :: HttpResponse()
	: HttpMessage( eResponse )
{
	mStatusCode = 200;
	snprintf( mReasonPhrase, sizeof( mReasonPhrase ), "%s", "OK" );
}
HttpResponse :: ~HttpResponse()
{
}
void HttpResponse :: setStatusCode( int statusCode )
{
	mStatusCode = statusCode;
}
int HttpResponse :: getStatusCode() const
{
	return mStatusCode;
}
void HttpResponse :: setReasonPhrase( const char * reasonPhrase )
{
	snprintf( mReasonPhrase, sizeof( mReasonPhrase ), "%s", reasonPhrase );
}
const char * HttpResponse :: getReasonPhrase() const
{
	return mReasonPhrase;
}

string HttpResponse::toString() const
{
	std::ostringstream ostream;
	ostream << getVersion() << " " << getStatusCode() << " " << getReasonPhrase() << "\r\n";
	map<string, string>::const_iterator it = mapHeaderNameValue.begin();
	while (it != mapHeaderNameValue.end())
	{
		ostream << it->first << ":" << it->second << "\r\n";
		it++;
	}
	string json = ostream.str();
	return json;
}

//---------------------------------------------------------
